home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UGroupDocCmds.cp < prev    next >
Encoding:
Text File  |  1994-02-20  |  16.6 KB  |  616 lines  |  [TEXT/MPS ]

  1. // Copyright © 1992 Peter Speck, speck@dat.ruc.dk. All rights reserved.
  2. // UGroupDocCmds.cp
  3.  
  4. #include "UGroupDocCmds.h"
  5. #include "UNewsAppl.h"
  6. #include "UNntp.h"
  7. #include "UProgress.h"
  8. #include "UGroupDoc.h"
  9. #include "UDiscList.h"
  10. #include "UDiscListView.h"
  11. #include "UGroupTree.h"
  12. #include "UHeaderList.h"
  13. #include "FileTools.h"
  14. #include "Tools.h"
  15. #include "NetAsciiTools.h"
  16. #include "UThread.h"
  17. #include "UPrefsFileMgr.h"
  18.  
  19. #include <ErrorGlobals.h>
  20. #include <RsrcGlobals.h>
  21. #include <Script.h>
  22. #include <Folders.h>
  23. #include <ToolUtils.h>
  24. #include <Errors.h>
  25.  
  26.  
  27. #pragma segment MyGroup
  28.  
  29. #define qDebugShowDiscListWindow qDebug & 0
  30. #define qDebugHeaderProgress qDebug & 0
  31. #define qDebugDiscListCreateNewDiscussion qDebug & 0
  32.  
  33. void GoPublicFile(const CStr255 &groupDotName, FSSpec &spec)
  34. {
  35.     FailOSErr(FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, spec.vRefNum, spec.parID));
  36.     gCurThread->CheckYield();
  37.     CStr255 folderName;
  38.     MyGetIndString(folderName, kPublicFilesFolderName);
  39.     if (UsesFakeNntp())
  40.         folderName += " (DB)";
  41.     CopyCString2String(folderName, spec.name);
  42.     gCurThread->CheckYield();
  43.     GoInsideFolder(spec);
  44.     gCurThread->CheckYield();
  45.     GoGroupFile(groupDotName, spec);
  46.     gCurThread->CheckYield();
  47. }
  48.  
  49. //--------------------------------------------------------------------------------
  50.  
  51. TOpenGroupCommand::TOpenGroupCommand()
  52. {
  53. }
  54.  
  55. pascal void TOpenGroupCommand::Initialize()
  56. {
  57.     inherited::Initialize();
  58.     fDoc = nil;
  59.     fNntp = nil;
  60. }
  61.  
  62. void TOpenGroupCommand::IOpenGroupCommand(const CStr255 &name, 
  63.             Boolean updateDatabase, Boolean updateOnly, Boolean saveIfNonexisting)
  64. {
  65.     inherited::ICommand(cOpenGroupDoc, nil, false, false, nil);
  66.     fGroupDotName = name;
  67.     fUpdateDatabase = updateDatabase;
  68.     fUpdateOnly = updateOnly;
  69.     fSaveIfNonexisting = saveIfNonexisting;
  70. }
  71.  
  72. pascal void TOpenGroupCommand::Free()
  73. {
  74.     gNntpCache->ReturnNntp(fNntp); fNntp = nil;
  75.     fDoc = nil;
  76.     inherited::Free();
  77. }
  78.  
  79. pascal void TOpenGroupCommand::DoIt()
  80. {
  81.     FailInfo fi;
  82.     if (fi.Try())
  83.     {
  84.         if (!fUpdateOnly)
  85.             gCurProgress->SetStandardProgressType();
  86.         CStr255 dotName(fGroupDotName);
  87.         gCurProgress->SetTitle(dotName);
  88.         gCurProgress->SetText(kOpenGroupReadsFile);
  89.         gCurProgress->SetWorkToDo(kCandyStribes);
  90.         Boolean progressWindowToFront = !fUpdateOnly;
  91.         gCurProgress->StartProgress(progressWindowToFront);
  92.         OpenDocument();
  93.         gCurThread->CheckYield();
  94.         UpdateDiscussions();
  95.         fDoc->DoPostUpdate(fUpdateOnly);
  96.         gCurThread->CheckYield();
  97.         if (fUpdateOnly)
  98.             fDoc->CloseAndFree();
  99.         else
  100.         {
  101.             ShowDiscListWindow();
  102.             CStr255 dotName(fGroupDotName);
  103.             gNewsAppl->QuickUpdateGroupHasNoNewArticles(dotName);
  104.             gNewsAppl->GetGroupTree()->SetLastReadArticleID(dotName, fLastID);
  105.         }
  106.         fi.Success();
  107.     }
  108.     else // fail
  109.     {
  110.         gCurProgress->WorkDone();
  111.         if (fDoc)
  112.         {
  113.             fDoc->SetChangeCount(0); // abort changes -> avoid save
  114.             fDoc->CloseAndFree(); fDoc = nil;
  115.         }
  116.         gErrorParm3 = fGroupDotName;
  117.         FailNewMessage(fi.error, fi.message, messageOpenGroupFailed); 
  118.     }
  119. }
  120.  
  121. #if qDebugShowDiscListWindow
  122. void GetWindowName(WindowPtr window, CStr255 &name)
  123. {
  124.     StringPtr p = (StringPtr) *WindowPeek(window)->titleHandle;
  125.     name = p;
  126. }
  127. #endif
  128.  
  129. void TOpenGroupCommand::ShowDiscListWindow()
  130. {
  131.     EventRecord event; // we may not do this stuff when any update events is pending
  132. //    CEventListIterator iter(fEventList);
  133.     while (true)
  134.     {
  135.         Boolean hasEvent = false;
  136. /*
  137.         for (TEvent *itsEvent = iter.FirstEvent(); iter.More(); itsEvent = iter.NextEvent())
  138.         {
  139.             if (itsEvent->IsReadyToExecute())
  140.             {
  141. #if qDebugShowDiscListWindow
  142.         fprintf(stderr, "ShowDiscListWindow (cmd = $%lx) waits for TEvent to be processed\n", long(this));
  143. #endif
  144.                 hasEvent = true;
  145.                 break;
  146.             }
  147. */
  148.         if (!hasEvent)
  149.         {
  150.             if (!EventAvail(activMask, event))
  151.                 break;
  152. //#if qDebugShowDiscListWindow
  153. #if qDebug
  154.         fprintf(stderr, "ShowDiscListWindow (cmd = $%lx) waits for activate event to be processed\n", long(this));
  155. #endif
  156.         }
  157.         gCurThread->CheckYield();
  158.     }
  159. #if qDebugShowDiscListWindow
  160.     CStr255 wname;
  161. #endif
  162.     fDoc->fDiscListView->UpdateList();
  163.     TWindow *pw = gCurProgress->GetWindow();
  164.     Boolean gotIt = false;
  165.     if (pw)
  166.     {
  167.         GrafPtr progressGrafP = pw->GetGrafPort();
  168.         if (progressGrafP)
  169.         {
  170.             WindowPtr discListWindowP = fDoc->fDiscListWindow->GetGrafPort();
  171. #if qDebugShowDiscListWindow
  172.             GetWindowName(progressGrafP, wname);
  173.             fprintf(stderr, "Progress window $%lx, name = %s\n", long(progressGrafP), (char*)wname);
  174.             GetWindowName(discListWindowP, wname);
  175.             fprintf(stderr, "DiscListWindow  $%lx, name = '%s'\n", long(discListWindowP), (char*)wname);
  176. #endif
  177.             WindowPtr prevWindow = nil;
  178.             WindowPtr sendBehindWindow = nil; // nil == not found
  179.             CWMgrIterator iter;
  180.             for (WindowPtr aWindowPtr = iter.FirstWMgrWindow(); iter.More(); aWindowPtr = iter.NextWMgrWindow())
  181.             {
  182. #if qDebugShowDiscListWindow
  183.                 GetWindowName(aWindowPtr, wname);
  184.                 fprintf(stderr, "Compare window $%lx,  name = '%s' - ", long(aWindowPtr), (char*)wname);
  185. #endif
  186.                 if (aWindowPtr == progressGrafP)
  187.                 {
  188. #if qDebugShowDiscListWindow
  189.                     fprintf(stderr, "found as progress window\n");
  190. #endif
  191.                     sendBehindWindow = prevWindow;
  192.                     break;
  193.                 }
  194.                 if (aWindowPtr == discListWindowP)
  195.                 {
  196. #if qDebugShowDiscListWindow
  197.                     fprintf(stderr, "is disclist, so it's fully ignored\n");
  198. #endif
  199.                 }
  200.                 else if (!WindowPeek(aWindowPtr)->visible)
  201.                 {
  202. #if qDebugShowDiscListWindow
  203.                     fprintf(stderr, "is invisible, so it's ignored\n");
  204. #endif
  205.                 }
  206.                 else
  207.                 {
  208. #if qDebugShowDiscListWindow
  209.                     fprintf(stderr, "skipped and stored\n");
  210. #endif
  211.                     prevWindow = aWindowPtr;
  212.                 }
  213.             }
  214. #if qDebugShowDiscListWindow
  215.             fprintf(stderr, "Done with progress window (hides it)\n");
  216. #endif
  217.             gCurProgress->WorkDone();
  218.             if (sendBehindWindow)
  219.             {
  220.                 if (aWindowPtr != progressGrafP)
  221.                 {
  222. #if qDebug
  223.                     ProgramBreak("FrontWindow was not progress window");
  224. #endif
  225.                     Failure(minErr, 0);
  226.                 }
  227. //                if (MAFrontWindow() == aWindowPtr)
  228. //                    pw->Activate(false); //hack, as it otherwise didn't gets deactivated
  229.                 if (discListWindowP == sendBehindWindow)
  230.                 {
  231. #if qDebug
  232.                     ProgramBreak("fDiscListwindow was sendBehindWindow!");
  233. #endif
  234.                     Failure(minErr, 0);
  235.                 }
  236. #if qDebugShowDiscListWindow
  237.                 GetWindowName(discListWindowP, wname);
  238.                 fprintf(stderr, "Does SendBehind:\n");
  239.                 fprintf(stderr, "-  DiscListWindow = $%lx, name = '%s'\n", long(discListWindowP), (char*)wname);
  240.                 GetWindowName(sendBehindWindow, wname);
  241.                 fprintf(stderr, "-  BeHindWindow   = $%lx, name = %s\n", long(sendBehindWindow), (char*)wname);
  242. #endif
  243.                 SendBehind(fDoc->fDiscListWindow->GetGrafPort(), sendBehindWindow);
  244.             }
  245.             else
  246.             {
  247.                 fDoc->fDiscListWindow->Select();
  248.             }
  249.             fDoc->fDiscListWindow->Open();
  250.             gotIt = true;
  251.         }
  252.     }
  253.     if (!gotIt)
  254.     {
  255.         fDoc->fDiscListWindow->Select();
  256.         fDoc->fDiscListWindow->Open();
  257.     }
  258. }
  259.  
  260. //..........................................................................................
  261. void TOpenGroupCommand::OpenDocument()
  262. {
  263.     TGroupDoc *doc = nil;
  264.     VOLATILE(doc);
  265.     TFile *statFile = nil;
  266.     VOLATILE(statFile);
  267.     TFile *dbFile = nil;
  268.     VOLATILE(dbFile);
  269.     FailInfo fi;
  270.     if (fi.Try())
  271.     {
  272.         FSSpec spec;
  273.         CStr255 dotName(fGroupDotName);
  274.     
  275.         gCurThread->CheckYield();
  276.         GetPrefsDocLocation(spec);
  277.         gCurThread->CheckYield();
  278.         GoGroupFile(dotName, spec);
  279.         gCurThread->CheckYield();
  280.         statFile = NewFile(kArticleStatusFileType, kSignature, kUsesDataFork, noResourceFork, kDataOpen, !kRsrcOpen);
  281.         statFile->Specify(spec);
  282.         statFile->SetPermissions(fsRdWrPerm, fsRdWrPerm);
  283.         TDocument *otherDoc = gApplication->FindDocument(statFile);
  284.         if (otherDoc)
  285.         {
  286.             if (!fUpdateOnly)
  287.                 otherDoc->OpenAgain(cOpenGroupDoc, nil);
  288.             Failure(0, 0); // silent like cancel
  289.         }
  290.         ResolveAliasFile(statFile);
  291.         fGotStatFile = FileExist(statFile);
  292.         gCurThread->CheckYield();
  293.         
  294.         GoPublicFile(dotName, spec);
  295.         dbFile = NewFile(kGroupDBFileType, kSignature, kUsesDataFork, noResourceFork, !kDataOpen, !kRsrcOpen);
  296.         dbFile->Specify(spec);
  297.         dbFile->SetPermissions(fsRdPerm, fsRdPerm);
  298.         ResolveAliasFile(dbFile);
  299.         fGotDBFile = FileExist(dbFile);
  300.         gCurThread->CheckYield();
  301.  
  302.         TFile *f1 = statFile; statFile = nil; // we don't own that one anymore!
  303.         TFile *f2 = dbFile; dbFile = nil;     // nor that one
  304.         TGroupDoc *aGroupDoc = new TGroupDoc();
  305.         aGroupDoc->IGroupDoc(f1, f2, dotName);
  306.         doc = aGroupDoc;
  307.         gCurThread->CheckYield();
  308.         
  309.         if (fGotStatFile)
  310.             doc->fFileHandler->ReadFile(kForDisplay);
  311.         gCurThread->CheckYield();
  312.         if (fGotDBFile)
  313.             doc->fDBFileHandler->ReadFile(kForDisplay);
  314.         doc->SetChangeCount(0);
  315.  
  316.         gCurThread->CheckYield();
  317.         doc->DoMakeViews(kForDisplay);
  318.         FailSpaceIsLow();                // Fail if the document leaves us with no memory
  319.         doc->DoPostMakeViews(kForDisplay);
  320.         fDoc = doc;
  321.         fi.Success();
  322.     }
  323.     else // fail
  324.     {
  325.         if (doc)
  326.         {
  327.             doc->SetChangeCount(0); // abort changes -> avoid save
  328.             doc->CloseAndFree(); doc = nil;
  329.         }
  330.         else
  331.         {
  332.             FreeIfObject(statFile); statFile = nil;
  333.             FreeIfObject(dbFile); dbFile = nil;
  334.         }
  335.         gErrorParm3 = fGroupDotName;
  336.         FailNewMessage(fi.error, fi.message, messageOpenGroupFailed); 
  337.     }
  338. }
  339. //..........................................................................................
  340.  
  341. Boolean TOpenGroupCommand::StartUpdate()
  342. {
  343.     gCurProgress->SetText(kOpenGroupChecksForNew);
  344.     gCurThread->CheckYield();
  345.     CStr255 dotName(fGroupDotName);
  346.     fNntp->SetGroup(dotName, true);
  347.     long firstID, lastID;
  348.     fNntp->GetGroupInfo(firstID, lastID);
  349.     long numArticles = Min(lastID - firstID + 1, gPrefs->GetLongPrefs('Mart'));
  350.     firstID = lastID - numArticles + 1;
  351.     fFirstID = firstID;
  352.     fLastID = lastID;
  353.     fDoc->fArticleStatus->SetNewRange(fFirstID, fLastID); // always do this as files can be deleted
  354.     fDoc->fOldArticleStatus->SetNewRange(fFirstID, fLastID);
  355.     if (fFirstID > fLastID || fFirstID == 0)
  356.     {
  357. #if qDebug
  358.         fprintf(stderr, "UPDATE: Empty group: fFirstID = %ld > fLastID = %ld\n", fFirstID, fLastID);
  359. #endif
  360.         fDoc->fFirstArticleID = fFirstID;
  361.         fDoc->fLastArticleID = fLastID;
  362.         fDoc->fSubjectHdr->DeleteAll();
  363.         fDoc->fFromHdr->DeleteAll();
  364.         fDoc->fMsgIDHdr->DeleteAll();
  365.         fDoc->fRefsHdr->DeleteAll();
  366.         fDoc->fDiscList->DeleteAll();
  367.         return false; // empty group
  368.     }
  369.     if (fFirstID >= fDoc->fFirstArticleID && fFirstID <= fDoc->fLastArticleID + 1)
  370.     {
  371.         // could I do more sanity checking here??
  372.         if (fLastID <= fDoc->fLastArticleID)
  373.         {
  374. #if qDebug
  375.             fprintf(stderr, "UPDATE: no new stuff\n");
  376. #endif
  377.             return false; // no new stuff (funny, we don't delete old stuff, when there aren't new stuff)
  378.         }
  379. #if qDebug
  380.         fprintf(stderr, "UPDATE: Old: %ld -> %ld,   new: %ld -> %ld", fDoc->fFirstArticleID, fDoc->fLastArticleID, fFirstID, fLastID);
  381. #endif
  382.         fFirstNewID = fDoc->fLastArticleID + 1;
  383.         fDoc->fFirstArticleID = fFirstID;
  384.         fDoc->fLastArticleID = fLastID;
  385. #if qDebug
  386.         fprintf(stderr, ",  update: %ld -> %ld\n\n", fFirstNewID, fLastID);
  387. #endif
  388.     }
  389.     else // otherwise the stuff we've got is out of synch
  390.     {
  391. #if qDebug
  392.         fprintf(stderr, "UPDATE: Delete all old headers (old: %ld -> %ld, new: %ld -> %ld)\n", fDoc->fFirstArticleID, fDoc->fLastArticleID, fFirstID, fLastID);
  393. #endif
  394.         fFirstNewID = fFirstID;
  395.         fDoc->fFirstArticleID = fFirstID;
  396.         fDoc->fLastArticleID = fLastID;
  397.         fDoc->fSubjectHdr->DeleteAll();
  398.         fDoc->fFromHdr->DeleteAll();
  399.         fDoc->fMsgIDHdr->DeleteAll();
  400.         fDoc->fRefsHdr->DeleteAll();
  401.         fDoc->fDiscList->DeleteAll();
  402.     }
  403.     long work = (fLastID - fFirstNewID + 1) * (4 + 1); // headers 4, distribute 1
  404.     gCurProgress->SetWorkToDo(work);
  405. #if qDebug
  406.     fprintf(stderr, "Work to do: %ld:  %ld %ld\n", work, fFirstNewID, fLastID);
  407. #endif
  408.     return true;
  409. }
  410.  
  411. void TOpenGroupCommand::UpdateHeaderLists()
  412. {
  413.     gCurProgress->SetText(kOpenGroupFetchesHeaders);
  414.     gCurThread->CheckYield();
  415.     Handle h = nil;
  416.     long firstNewID = fFirstNewID;
  417.     long lastID = fLastID;
  418.     fDoc->fMsgIDHdr->DeleteAll();
  419.     fDoc->fRefsHdr->DeleteAll();
  420. #if qDebugHeaderProgress
  421.     fprintf(stderr, "Has to fetch %ld headers  %ld %ld\n", fLastID - firstNewID, firstNewID, fLastID);
  422. #endif
  423.     while (true)
  424.     {
  425.         lastID = Min(fLastID, firstNewID + gPrefs->GetLongPrefs('XHSz') - 1);
  426. #if qDebugHeaderProgress
  427.         fprintf(stderr, "Fetches %ld headers  %ld - %ld\n", lastID - firstNewID + 1, firstNewID, lastID);
  428.         fprintf(stderr, "Before work: %ld\n", gCurProgress->GetWorkDone());
  429. #endif
  430.  
  431.          // header names are hardcoded, but they are hardcoded in RFC 977 too
  432.         h = fNntp->GetHeaderList("Subject", firstNewID, lastID);
  433.         TranslateViaTable(gNetAscii2Mac, *h, GetHandleSize(h));
  434.         fDoc->fSubjectHdr->AddNewHeaders(h, fFirstID, firstNewID, lastID);
  435.         h = DisposeIfHandle(h);
  436.         
  437.         h = fNntp->GetHeaderList("From", firstNewID, lastID);
  438.         TranslateViaTable(gNetAscii2Mac, *h, GetHandleSize(h));
  439.         fDoc->fFromHdr->AddNewHeaders(h, fFirstID, firstNewID, lastID);
  440.         h = DisposeIfHandle(h);
  441.     
  442.         h = fNntp->GetHeaderList("Message-ID", firstNewID, lastID);
  443.         fDoc->fMsgIDHdr->AddNewHeaders(h, fFirstID, firstNewID, lastID);
  444.         h = DisposeIfHandle(h);
  445.  
  446.         h = fNntp->GetHeaderList("References", firstNewID, lastID);
  447.         fDoc->fRefsHdr->AddNewHeaders(h, fFirstID, firstNewID, lastID);
  448.         h = DisposeIfHandle(h);
  449.  
  450. #if qDebugHeaderProgress
  451.         fprintf(stderr, "After work: %ld\n", gCurProgress->GetWorkDone());
  452. #endif
  453.         if (lastID >= fLastID)
  454.             break;
  455.         firstNewID = lastID + 1;
  456.         lastID = fLastID;
  457.     }
  458. }
  459.  
  460. void TOpenGroupCommand::CreateNewDiscussion(long id)
  461. {
  462.     HandleOffsetLength discIDhol;
  463.     fDoc->fRefsHdr->AtGet(id, discIDhol);
  464.     if (discIDhol.fLength)
  465.     {
  466.         Ptr p = *discIDhol.fH + discIDhol.fOffset;
  467.         while (discIDhol.fLength && *p != '<')
  468.         {
  469.             ++p;
  470.             ++discIDhol.fOffset;
  471.             --discIDhol.fLength;
  472.         }
  473. #if qDebug
  474.         if (!discIDhol.fLength)
  475.         {
  476.             fprintf(stderr, "Avoids bogous References header:");
  477.             HandleOffsetLength hol;
  478.             fDoc->fRefsHdr->AtGet(id, hol);
  479.             CStr255 s;
  480.             CopyHolToCStr255(hol, s);
  481.             fprintf(stderr, "   '%s'\n", (char*)s);
  482.         }
  483. #endif
  484.     }
  485.     if (discIDhol.fLength == 0)
  486.     {
  487.         fDoc->fMsgIDHdr->AtGet(id, discIDhol);
  488.         if (discIDhol.fLength == 0)
  489.             return; // non-existent article
  490.         Ptr p = *discIDhol.fH + discIDhol.fOffset;
  491.         while (discIDhol.fLength && *p != '<')
  492.         {
  493.             ++p;
  494.             ++discIDhol.fOffset;
  495.             --discIDhol.fLength;
  496.         }
  497.         if (!discIDhol.fLength)
  498.         {
  499. #if qDebug
  500.             fprintf(stderr, "Avoids bogous Message-id header:");
  501.             HandleOffsetLength hol;
  502.             fDoc->fRefsHdr->AtGet(id, hol);
  503.             CStr255 s;
  504.             CopyHolToCStr255(hol, s);
  505.             fprintf(stderr, "   '%s'\n", (char*)s);
  506. #endif
  507.             return;
  508.         }
  509.     }
  510.  
  511.     HandleOffsetLength nameHol;
  512.     fDoc->fSubjectHdr->AtGet(id, nameHol);
  513.     long discIndex = fDoc->fDiscList->CreateDiscussion(id, discIDhol, nameHol);
  514. }
  515.     
  516. void TOpenGroupCommand::DistributeArticle(long id)
  517. {
  518. #if qDebug & 0
  519.     fprintf(stderr, "id = %ld\n", id);
  520. #endif
  521.     HandleOffsetLength msgIDHol;
  522.     fDoc->fMsgIDHdr->AtGet(id, msgIDHol);
  523.     if (msgIDHol.fLength == 0)
  524.         return; // non-existent article!
  525.  
  526.     HandleOffsetLength refsHol;
  527.     fDoc->fRefsHdr->AtGet(id, refsHol);
  528.  
  529.     ArrayIndex discIndex;
  530.     
  531.     if (refsHol.fLength && fDoc->fDiscList->TryFindDiscussion(refsHol, discIndex))
  532.     {
  533.         fDoc->fDiscList->AddArticle(discIndex, id);
  534.         return;
  535.     }
  536.     if (fDoc->fDiscList->TryFindDiscussion(msgIDHol, discIndex))
  537.     {
  538.         HandleOffsetLength nameHol;
  539.         fDoc->fSubjectHdr->AtGet(id, nameHol);
  540.         fDoc->fDiscList->AddArticleAsOriginator(discIndex, id, msgIDHol, nameHol);
  541.         return;
  542.     }
  543.     CreateNewDiscussion(id);
  544. #if qDebugDiscListCreateNewDiscussion
  545.     if (!fDoc->fDiscList->SanityCheck())
  546.         Failure(errDatabaseScrambled, 0);
  547. #endif
  548. }
  549.  
  550. void TOpenGroupCommand::DistributeNewArticles()
  551. {
  552.     gCurProgress->SetText(kOpenGroupUpdatesDatabase);
  553.     gCurThread->CheckYield();
  554.     short delta = 0;
  555.     for (long id = fFirstNewID; id <= fLastID; id++) 
  556.     {
  557.         DistributeArticle(id);
  558.         ++delta;
  559.         if (delta == 5)
  560.         {
  561.             gCurProgress->Worked(delta);
  562.             delta = 0;
  563.         }
  564.         gCurThread->CheckYield();
  565.     }
  566.     if (delta)
  567.         gCurProgress->Worked(delta);
  568. #if qDebug
  569.     gCurProgress->SetText("Sanity check after update");
  570.     if (!fDoc->fDiscList->SanityCheck())
  571.         Failure(errDatabaseScrambled, 0);
  572. #endif
  573. }
  574.  
  575. void TOpenGroupCommand::UpdateDiscussions()
  576. {
  577.     CStr255 dotName(fGroupDotName);
  578. #if qDebug
  579.     fprintf(stderr, "TOpenGroupCommand: Updates group '%s'\n", (char*)dotName);
  580.     if (!IsObject(fDoc))
  581.         ProgramBreak("fDoc is not object");
  582. #endif
  583.     FailInfo fi;
  584.     if (fi.Try())
  585.     {
  586.         fNntp = gNntpCache->GetNntp();
  587.         if (StartUpdate())
  588.         {
  589.             gCurProgress->SetText(kOpenGroupDeletesOld);
  590.             fDoc->fDiscList->DeleteOldStuff(fFirstID, fLastID); //?? maybe even if no new stuff??
  591.             UpdateHeaderLists();
  592.             DistributeNewArticles();
  593.             fDoc->Changed(cDatabaseUpdated, this);
  594.             gCurProgress->SetText(kOpenGroupSavesDatabase);
  595.             if (!fSaveIfNonexisting)
  596.             {
  597.                 fDoc->fSaveDatabase = fGotDBFile;
  598.                 fDoc->fSaveArticleStatus = fGotStatFile;
  599.             }
  600.             fDoc->SaveDocument(cSave);
  601.             gNewsAppl->GetGroupTree()->SetLastUpdatedArticleID(dotName, fLastID);
  602.         }
  603.         fDoc->fMsgIDHdr->DeleteAll(); // not needed anymore
  604.         fDoc->fRefsHdr->DeleteAll();
  605.         gNntpCache->ReturnNntp(fNntp); fNntp = nil;
  606.         fi.Success();
  607.     }
  608.     else // fail
  609.     {
  610.         gNntpCache->DiscardNntp(fNntp);
  611.         fNntp = nil;
  612.         gErrorParm3 = fGroupDotName;
  613.         FailNewMessage(fi.error, fi.message, messageUpdateGroupFailed);
  614.     }
  615. }
  616.